namespace Resharper.Abstractions.CodeAnnotations
{
  using System;
  using System.Collections.Generic;
  using System.Linq;
  using JetBrains.Annotations;
  using JetBrains.ReSharper.Psi;
  using JetBrains.ReSharper.Psi.CSharp.Tree;
  using JetBrains.ReSharper.Psi.Tree;
  using Resharper.Abstractions.CodeDom.TreeNodes;
  using Resharper.Abstractions.Daemons;

  /// <summary>Defines the <see cref="ClassAnnotationDaemonBase"/> class.</summary>
  public abstract class ClassAnnotationDaemonBase : DaemonBase
  {
    #region Constants and Fields

    /// <summary>The handlers field.</summary>
    private readonly List<ClassAnnotationHandler> handlers = new List<ClassAnnotationHandler>();

    #endregion

    #region Methods

    /// <summary>Handles the attribute.</summary>
    /// <param name="attributeName">The name.</param>
    protected abstract ClassAnnotationHandler HandleAttribute([NotNull] string attributeName);

    /// <summary>Processes the specified tree node.</summary>
    /// <param name="treeNode">The tree node.</param>
    protected override void Process(TreeNode treeNode)
    {
    }

    /// <summary>Processes the tree node.</summary>
    /// <param name="treeNode">The tree node.</param>
    protected override void ProcessTreeNode([NotNull] ITreeNode treeNode)
    {
      if (treeNode == null)
      {
        throw new ArgumentNullException("treeNode");
      }

      var classNode = treeNode as IClassDeclaration;
      if (classNode == null)
      {
        return;
      }

      var attributes = classNode.Attributes;

      this.GetHandlers(attributes);
      if (!this.handlers.Any())
      {
        return;
      }

      var processor = new RecursiveElementProcessor(this.ProcessNode);
      processor.Process(treeNode);

      this.handlers.Clear();
    }

    /// <summary>Gets the handlers.</summary>
    /// <param name="attributes">The attributes.</param>
    private void GetHandlers([NotNull] IEnumerable<IAttribute> attributes)
    {
      if (attributes == null)
      {
        throw new ArgumentNullException("attributes");
      }

      this.handlers.Clear();

      foreach (var attribute in attributes)
      {
        var name = attribute.Name.QualifiedName;
        if (!name.EndsWith("Attribute"))
        {
          name += "Attribute";
        }

        var handler = this.HandleAttribute(name);
        if (handler != null)
        {
          this.handlers.Add(handler);
        }
      }
    }

    /// <summary>Processes the node.</summary>
    /// <param name="treeNode">The tree node.</param>
    private void ProcessNode([NotNull] ITreeNode treeNode)
    {
      foreach (var handler in this.handlers)
      {
        handler.ProcessTreeNode(this, treeNode);
      }
    }

    #endregion
  }
}